import { Component, ComponentArgs } from "../filesystem"
import { IComponent, waitAnimationFrame } from "./sys";
import { DataProvider } from "./dataprovider";
import { AjaxArgs, Ajax } from "./ajax";
import { FileInfo } from "./fileinfo";

/**
 * Tree控件
 */
export class Tree extends IComponent {

    /**
     * 提供数据
     */
    private dataProvider: DataProvider<TreeItem>;

    /**
     *上次点击的item，在domBase的click中使用
     */
    private clickItem: TreeItem;

    /**
     * 保存所有已经加载过的item
     */
    private items: TreeItem[];

    /**
     * 树的点击事件，对整个树最外层点击事件不做处理，只处理内部item点击事件
     */
    public onclick: (e: Event, tree: Tree | TreeItem) => void | boolean;

    /**
     * 如果不为空，会尽快执行渲染，渲染完成后会将其置空
     */
    private renderPromise: Promise<any>;

    /**
     * 如果不为空会尽快执行刷新逻辑，一般都是在此promise执行完后产生render请求，随后产生renderPromise
     */
    private refreshPromise: Promise<any>;

    /**
     * changeToggleSelect(clickItem) 进行选中样式转换
     * changeToggleSelect(clickItem,0) 进行选中样式转换 进行展开样式转换
     * changeToggleSelect(clickItem,1) 进行选中样式转换 进行关闭样式转换
     * @param clickItem 被点击的item
     */
    public changeToggleSelect(clickItem: TreeItem, type?: number) {
        let oldClickItem = this.clickItem;
        this.clickItem = clickItem;
        if (oldClickItem !== null && oldClickItem !== undefined) {
            oldClickItem.removeClassName("tree_item_select");
            oldClickItem.setClassName("tree_item");
        }
        clickItem.removeClassName("tree_item");
        clickItem.setClassName("tree_item_select");

        if (type === 0) {
            /**
             * expand
             */
            clickItem.removeClassName("tree_item");
            clickItem.setClassName("tree_item_select");
            clickItem.div1.classList.remove("folder-close-left")
            clickItem.div1.classList.add("folder-expand-left")
            clickItem.div2.classList.remove("folder-close")
            clickItem.div2.classList.add("folder-expand")
            clickItem.isExpand = true;
        } else if (type === 1) {
            /**
             * close
             */
            clickItem.removeClassName("tree_item");
            clickItem.setClassName("tree_item_select");
            clickItem.div1.classList.remove("folder-expand-left");
            clickItem.div1.classList.add("folder-close-left");
            clickItem.div2.classList.remove("folder-expand");
            clickItem.div2.classList.add("folder-close");
            clickItem.isExpand = false;
        }
    }

    /**
     * 数据发生变化时，刷新页面
     */
    public refresh(item?: TreeItem) {
        //初始化和重新渲染的方法
        let promise: Promise<any>;
        if (!item) {
            promise = this.refreshPromise = this.dataProvider.fetchData().then((data) => {
                //初始化渲染，如果多次渲染在获取数据时不同步只保留最后一次refesh
                if (promise !== this.refreshPromise) return this;
                this.requestRender(null, data);
            })
        } else {
            promise = this.refreshPromise = this.dataProvider.fetchData({ parentData: item }).then((data) => {
                //对某个节点进行渲染
                if (promise !== this.refreshPromise) return this;
                this.requestRender(item, data);
            })
        }
        return this.refreshPromise = promise;
    }
    /**
     * 请求渲染
     */
    private requestRender(item?: TreeItem, items?: TreeItem[]) {
        //保证render都是串行执行的
        if (this.renderPromise)
            return this.renderPromise;
        this.renderPromise = waitAnimationFrame().then(() => {
            this.render(item, items);
            this.renderPromise = null;
        })
    }

    /**
     * 真正执行渲染的函数
     */
    private render(item?: TreeItem, items?: TreeItem[]) {
        if (!item) {
            //初始化树
            let aItems: TreeItem[] = this.items = [];
            items.forEach((item) => {
                this.domBase.appendChild(item.domBase);
                item.domParent = this.domBase;
                aItems.push(item);
            });
        } else {
            //往树中添加元素，从来未加载过的，需要执行此方法
            items.forEach((subItem) => {
                item.childs.push(subItem);
            })
            let beforeItem: Node;
            let aItems = this.items;
            if (item.domBase.nextSibling === null || item.domBase.nextSibling === undefined) {
                for (let i = 0; i < items.length; i++) {
                    this.domBase.appendChild(items[i].domBase);
                    aItems.push(items[i]);
                }
            } else {
                beforeItem = item.domBase.nextSibling;
                for (let i = 0; i < items.length; i++) {
                    this.domBase.insertBefore(items[i].domBase, beforeItem);
                    aItems.push(items[i])
                }
            }



        }

    }

    /**展开选中item
     * @param clickItem 需要展开的节点
     */
    public expand(clickItem: TreeItem): void {
        this.changeToggleSelect(clickItem, 0);
        if (clickItem.childs.length === 0) {
            this.refresh(clickItem);
        } else {
            clickItem.childs.forEach((item) => {
                item.domBase.style.display = "";
            })
        }

    }

    /**
     * 对点击的节点做关闭操作
     * @param clickItem 需要关闭的节点
     */
    public close(clickItem: TreeItem): void {
        this.changeToggleSelect(clickItem, 1);
        let aItems = this.items;
        let removeItem: TreeItem[] = [];
        let remainItem: TreeItem[] = []
        //当前点击item下的所有item都设置为不可见
        this.recurRemoveItems(removeItem, clickItem);
        remainItem = aItems.filter((item) => {
            return removeItem.indexOf(item) === -1;
        });
        this.items = remainItem;
        removeItem.forEach((item) => {
            item.domBase.style.display = "none"
        });
    }

    /**
     * 递归的形式移除所有关闭的item
     * @param removeItem 
     * @param item 
     */
    private recurRemoveItems(removeItem: TreeItem[], item: TreeItem) {
        if (item.childs.length === 0) return;
        item.childs.forEach((subItem) => {
            removeItem.push(subItem);
            if (subItem.childs.length !== 0) {
                this.recurRemoveItems(removeItem, subItem);
            }
        })
    }


    constructor(args: TreeArgs) {
        super(args)
        this._init(args);
    }

    /**
    * 根据dom树，找到第一个挂载了szobject的dom，并返回
    * @param e 
    */
    protected getTreeItemFromEvent(e: Event): TreeItem | Tree {
        let item = <HTMLElement>(e.srcElement || e.target);
        if (item.szobject !== null && item.szobject !== undefined) {
            return item.szobject;
        }
        while (item.parentElement !== null) {
            item = item.parentElement;
            if (item.szobject !== null && item.szobject !== undefined) {
                return item.szobject;
            }
        }
        return null;
    }

    /**
     *创建tree的domBase，因为条目是动态的，所以此处只绘制tree的静态部分
     */
    private _init(args: TreeArgs) {
        if (!!args.onclick) {
            this.onclick = args.onclick.bind(this);
        }
        this.id = args.id;
        if (args.classList === null || args.classList === undefined) {
            this.classList = [];
        } else {
            this.classList = args.classList;
        }
        this.classList.push("tree")
        let domBase = this._createDomBase(args);
        if (this.onclick !== null && this.onclick !== undefined) {
            domBase.onclick = this.doClick.bind(this);
        }
    }

    /**
     * 监听domBase上发生的click事件，并传递给构造参数指定的回调
     * @param e 
     */
    private doClick(e: Event) {
        let treeItem: TreeItem | Tree = this.getTreeItemFromEvent(e);
        if (treeItem instanceof Tree) {
            //如果是在树上点击，由于获取不到正在点击的item，故不作处理
        } else {
            if (treeItem === null || treeItem === null) {
                console.log("Tree设置错误");
            } else {
                this.onclick(e, treeItem);
            }
        }

    }

    protected _createDomBase(args: TreeArgs): HTMLElement {
        return super._createDomBase(args);
    }

    /**
     * 重新设置完数据源后，需要fetchData，然后重新渲染
     * @param dataProvider 
     */
    public setDataProvider(dataProvider: DataProvider<TreeItem>) {
        this.dataProvider = dataProvider;
        this.refresh();
    }
}

/**
 * 树的构造参数
 */
export interface TreeArgs extends ComponentArgs {

    dataProvider?: DataProvider<TreeItem>;
    /**
     * 传递树的item数组
     */
    items?: TreeItem[];

    /**
     * 树的id
     */
    id: string,

    onclick?: (e: Event, tree: TreeItem | Tree) => void | boolean;
}

/**
 * 树的item条目
 */
export class TreeItem extends IComponent {

    /**
     *当前TreeItem的缩进层级 
     */
    public marginGrade: number;

    /**
     * 当前TreeItem的描述
     */
    public text: string;

    /**
     * 当前节点是一个目录还是一个叶子节点
     */
    public type: TreeItemType;

    /**
     * 当前节点状态，只有type是folder的才有意义
     */
    public isExpand: boolean;

    /**
     * 挂载哪一个TreeItem下
     */
    public parentId: string;
    public id: string;

    /**
     * 挂靠在此节点下的父节点，供fetchData时使用
     */
    public childs: TreeItem[] = [];

    /**
     * 附加的属性
     */
    public extra?: Object;

    /**
     * 组成此控件的第一个内部div
     */
    public div1: HTMLDivElement;

    /**
     * 组成此控件的第二个内部div
     */
    public div2: HTMLDivElement;

    /**
     * 组成此控件的第三个div
     */
    public div3: HTMLDivElement;

    constructor(args: TreeItemArgs) {
        super(args)
        this._init(args);
    }

    private _init(args: TreeItemArgs) {
        this.marginGrade = args.marginGrade;
        this.text = args.text;
        this.type = args.type;
        this.isExpand = !(args.isExpand === null || args.isExpand === undefined || args.isExpand === false);
        this.parentId = args.parentId;
        this.id = args.id;
        (args.extra === null || args.extra === undefined) ? this.extra = null : this.extra = args.extra;
        if (args.classList === null || args.classList === undefined) {
            this.classList = [];
        } else {
            this.classList = args.classList;
        }
        this.domBase = this._createDomBase(args);
        this.domBase.szobject = this;
        this.domBase.style.marginLeft = this.marginGrade * 20 + "px"; 2
    };

    protected _createDomBase(args: TreeItemArgs): HTMLElement {
        /**
         * 此TreeItem控件是复合dom，子类重写此方法,不使用入的domBase
         */
        let item = document.createElement("div");
        let item1 = document.createElement("div");
        let item2 = document.createElement("div");
        let item3 = document.createElement("div");
        /**
         * 元素id设置
         */
        item.id = args.id;
        item1.id = item.id + "_1";
        item2.id = item.id + "_2";
        item3.id = item.id + "_3";
        /**
         * 通用样式设置
         */
        item.classList.add("tree_item");
        item1.classList.add("tree_icons");
        item2.classList.add("tree_icons");
        item3.classList.add("text");
        /**
         * 根据item构造参数进行特定样式设置
         */
        if (args.type === TreeItemType.FOLDER) {
            if (this.isExpand) {
                item1.classList.add("folder-expand-left");
                item2.classList.add("folder-expand");
            } else {
                item1.classList.add("folder-close-left");
                item2.classList.add("folder-close");
            }
        } else if (args.type === TreeItemType.FILE) {
            item2.classList.add("file")
        }
        item3.textContent = args.text;
        item.appendChild(item1);
        item.appendChild(item2);
        item.appendChild(item3);
        this.div1 = item1;
        this.div2 = item2;
        this.div3 = item3;
        item1 = null;
        item2 = null;
        item3 = null;
        /**
         * 添加外部传入的classList,外部传入的classList值影响item
         */
        this.classList.forEach((clazz) => {
            item.classList.add(clazz);
        })

        return item;
    }
}



/**
 * 树的item条目构造参数
 */
export interface TreeItemArgs extends ComponentArgs {
    /**
     * 缩进层级
     */
    marginGrade: number;

    /**
     * 该项描述，如果没有描述，传递" "
     */
    text: string;

    /**
     * 当前条目是节点还是目录
     */
    type: TreeItemType;

    /**
     * 是否构造展开的节点，默认不会
     */
    isExpand?: boolean;

    /**
     * 当前条目的父节点的id，顶级节点为0
     */
    parentId: string;

    /**
     * 额外的参数
     */
    extra?: Object;
}

/**
 * TreeItem的类型
 */
export enum TreeItemType {
    /**
     * 此类别TreeItem包含其他TreeItem条目
     */
    FOLDER,

    /**
     * 此类别TreeItem不包含其他TreeItem条目
     */
    FILE
}
